/**
 * @file swei_socket.c
 * @brief 
 * @author  ()
 * @version 1.0
 * @date 2024-09-13
 * 
 * @copyright Copyright (C) 2019 Queclink Wireless Solutions (ShenZhen) Co., Ltd. All Rights Reserved.
 * 
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2024-09-13 <td>1.0     <td>     <td>内容
 * </table>
 */

#include "include/port/swei_log.h"
#include "include/swei_socket.h"
#include "include/port/swei_wrapper.h"
#include "include/swei_cert.h"
#include "mmgr/include/mmgr_misc.h"

#define SOCKET_INVALID_ID -1 /* 无效的socketID */

typedef struct
{
    bool is_prot_err;    /* 是否协议错误 */
    bool is_enabled;     /* TCP是否激活. */
    bool to_close;       /* 是否要断开（目前没有需要关闭） */
    bool connect_status; /* 网络连接状态 */
    int32_t socket_id;       /* MQTT 服务器 socket */
    int32_t last_socket_id;  /* 记录最后一次创建的 socket */
    uint32_t last_close_time;
} mmgr_skt_cps_t;

static mmgr_skt_cps_t skt_cps = {0};
ssl_cfg_t ssl_cfg;

void swei_socket_ssl_init(ssl_cfg_t *cfg);
void swei_socket_ssl_free(ssl_cfg_t *cfg);

/*
 * @brief socket初始化.
 */
void swei_socket_init(void)
{
    swei_socket_ssl_init(&ssl_cfg); /* 只能初始化一次 */

    skt_cps.is_enabled = true; /* 目前默认一直使能开启 */
    skt_cps.to_close = false;
    skt_cps.socket_id = SOCKET_INVALID_ID;
}

/*
 * @brief   socket动态分配释放.
 */
void swei_socket_deinit(void)
{
    swei_socket_close(true);
    swei_socket_ssl_free(&ssl_cfg);
}


/*
 * @brief 配置SSL参数，链接文件路径.
 */
void swei_socket_ssl_init(ssl_cfg_t *cfg)
{
    if(NULL == cfg)
        return;
        
    cfg->auth_mode = SSL_AUTH_NONE;
    cfg->is_sni_en = true;

    if (ssl_cfg.ca_cert)
    {
        swei_free(cfg->ca_cert);
        cfg->ca_cert = NULL;
    }

    if (cfg->client_cert)
    {
        swei_free(cfg->client_cert);
        cfg->client_cert = NULL;
    }

    if (cfg->client_key)
    {
        swei_free(cfg->client_key);
        cfg->client_key = NULL;
    }

    cfg->ca_cert = swei_malloc(strlen(CA_FILE_NAME) + 1);
    cfg->client_cert = swei_malloc(strlen(CLIENT_CERT_FILE_NAME) + 1);
    cfg->client_key = swei_malloc(strlen(CLIENT_KEY_FILE_NAME) + 1);

    memset(cfg->ca_cert, 0, strlen(CA_FILE_NAME) + 1);
    memset(cfg->client_cert, 0, strlen(CLIENT_CERT_FILE_NAME) + 1);
    memset(cfg->client_key, 0, strlen(CLIENT_KEY_FILE_NAME) + 1);

    memcpy(cfg->ca_cert, CA_FILE_NAME, strlen(CA_FILE_NAME));
    memcpy(cfg->client_cert, CLIENT_CERT_FILE_NAME, strlen(CLIENT_CERT_FILE_NAME));
    memcpy(cfg->client_key, CLIENT_KEY_FILE_NAME, strlen(CLIENT_KEY_FILE_NAME));
}

/*
 * @brief   ssl配置释放.
 */
void swei_socket_ssl_free(ssl_cfg_t *cfg)
{
    /* !!! 释放后必须重新创建才能去创建TLS的socket */
    if(NULL == cfg)
        return;

    if (NULL != cfg->ca_cert)
    {
        swei_free(cfg->ca_cert);
        cfg->ca_cert = NULL;
    }

    if (NULL != cfg->client_cert)
    {
        swei_free(cfg->client_cert);
        cfg->client_cert = NULL;
    }

    if (NULL != cfg->client_key)
    {
        swei_free(cfg->client_key);
        cfg->client_key = NULL;
    }
}


#if 0
/// @brief 
/// @param  
void swei_socket_ssl_upload_cert(void)
{
    int32_t result = 0;

    ssl_cert_t ssl_cert;
    // upload cert file
    swei_socket_cert_get(&ssl_cert);

    if (ssl_cert.is_ca_need)
    {
        /* 上传证书 */
        result = swei_file_upload(ssl_cfg.ca_cert, (uint8_t *)ssl_cert.ca_ptr,
                                    strlen((char const *)ssl_cert.ca_ptr));
        if (0 != result)
        {
            /* 启动上传失败, 等待重试 */
            log_e("Server CA upload start fail{%d}\r\n", result);
            return;
        }
        else
        {
            log_i("Server CA upload success\r\n");
        }
    }

    if (ssl_cert.is_cert_need)
    {
        /* 上传证书 */
        result = swei_file_upload(ssl_cfg.client_cert, (uint8_t *)ssl_cert.cert_ptr,
                                    strlen((char const *)ssl_cert.cert_ptr));
        if (0 != result)
        {
            /* 启动上传失败, 等待重试 */
            log_e("Client CERT upload start fail{%d}\r\n", result);
            return;
        }

        /* 上传证书 */
        result = swei_file_upload(ssl_cfg.client_key, (uint8_t *)ssl_cert.key_ptr,
                                    strlen((char const *)ssl_cert.key_ptr));
        if (0 != result)
        {
            /* 启动上传失败, 等待重试 */
            log_e("Client CERT upload start fail{%d}\r\n", result);
            return;
        }
        
        log_i("Client cert upload success\r\n");
    }

}
#endif

/*
 * @brief   链路关闭函数.
 * @note    1.该函数涉及到类型强转换,故应用者需细心;
 * @param   force: [I], true :表示忽略结果,强制认为关闭成功(用于modem的重启与关机),
                        false:保留结果,若关闭失败,将会继续关闭;
 */
void swei_tcp_close(bool force)
{
    int32_t res;

    if (!socket_is_valid(skt_cps.socket_id))
        return;

    /* 先注销MQTT服务，再关闭socket */
    swei_socket_distablish_hook();

    /* 关闭socket */
    res = swei_socket_close(skt_cps.socket_id);
    if (0 == res)
        skt_cps.socket_id = SOCKET_INVALID_ID;
    else
        log_e("modem socket close failed\r\n");
}

/* 从 TCP 服务器读数据 */
ssize_t swei_tcp_read(void *buff, size_t size)
{
    /* 连接是否存在 */
    if (!socket_is_valid(skt_cps.socket_id) || skt_cps.is_prot_err)
        return -ENODEV;

    ssize_t res = swei_socket_read(skt_cps.socket_id, buff, size);
    if (-ENODEV == res) {
        skt_cps.is_prot_err = true; /* 读取判断没有该连接, 标记错误, 需要重新创建 */
        log_e("socket[%d] error close, need to be rebuilt.\r\n", skt_cps.socket_id);
    }

#ifndef NDEBUG
    if (res > 0) {
        char show_buf[512] = {0};
        uint8_t *byte = (uint8_t *)buff;
        for(uint16_t len = 0; len < res; len++)
        {   
            snprintf(&show_buf[2 * len], sizeof(show_buf) - 2 * len, "%02x ", byte[len]);
        }
        log_d("socket[%d] read [len:%d]: %s", skt_cps.socket_id, res, show_buf);
    } 
#endif      
    return res;
}

#define MAX_TCP_TIMEOUT (5000)

/* 发送数据到 TCP 服务器 */
ssize_t swei_tcp_write(const void *buff, size_t size)
{
    if (!socket_is_valid(skt_cps.socket_id) || skt_cps.is_prot_err)
        return -ENODEV;
        
    return swei_socket_write_timeout(skt_cps.socket_id, buff, size, MAX_TCP_TIMEOUT);
}

/* 查询 TCP 连接发送状态 */
int32_t swei_tcp_write_query(void)
{
    return swei_socket_write_query(skt_cps.socket_id);
}

/*************************************************************************
 * 函数名称：mmgr_socket_update
 * 函数功能：modem socket状态变化更新事件
 * 输入参数：id-事件类型
 * 返 回 值：void
 *************************************************************************/
void swei_socket_update(stk_cmd_t id, int32_t prm, void *data, size_t size)
{
    int32_t result = 0;

    switch (id)
    {
        case M_SOC_STAT:
            if (SOC_CONNING == (int32_t)data)
            {
                /* 这里只关注为 MQTT 服务器创建的 socket */
                if (prm == skt_cps.socket_id)
                { // && !test_server_mgr.enable) {
                    log_d("swei socket register result: %d\r\n", result);
                    /* socket 创建后建立服务器连接 */
                    swei_socket_enstablish_hook();
                }
                skt_cps.connect_status = true; /* 网络连接 */
                log_i("########### socket[%d]  connect  #############\r\n", prm);
            }
            else if (SOC_UNCONN == (int32_t)data)
            {
                if (prm == skt_cps.socket_id)
                {
                    log_d("swei socket unregister result: %d\r\n", result);

                    skt_cps.socket_id = SOCKET_INVALID_ID;
                    skt_cps.to_close = false;
                    skt_cps.connect_status = false; /* 断开网络 */

                    /* socket 关闭后断开服务器的链接 */
                    swei_socket_distablish_hook();
                }

                log_i("########### socket[%d] disconnect #############\r\n", prm);
            }
        break;

        default:
            break;
    }
}

void swei_conn_srv_handler(void)
{
    static uint32_t time_id = 0;           /* 创建失败时定时打印 */
    static uint32_t socket_valid_time = 0; /* 定时查询socket是否有效 */
    unsigned char ip[IP_LEN + 1] = {0};
    uint16_t port = 0;
    uint8_t is_ssl = 0;
    // int32_t result = 0;

    if (skt_cps.is_prot_err)
    {
        /* 通讯错误, 关闭旧的 socket, 重新创建 */
        swei_tcp_close(false);
        skt_cps.is_prot_err = false;
        log_i("Prot error,close socket\r\n");
    }

    /* 定时查询当前socket是否存在 */
    if (socket_is_valid(skt_cps.socket_id))
    {
        if (0 == socket_valid_time)
            socket_valid_time = swei_getsec();

        if (swei_timeout_sec(socket_valid_time, 180))
        {
            if (-ENODEV == swei_socket_write_query(skt_cps.socket_id))
            {
                swei_tcp_close(false);
                log_e("Socket id:%d not finded in sdk\r\n", skt_cps.socket_id);
                skt_cps.socket_id = SOCKET_INVALID_ID;
                skt_cps.to_close = false;
            }
            else
            {
                log_e("Socket id:%d finded in sdk\r\n", skt_cps.socket_id);
            }
            socket_valid_time = swei_getsec();
        }
    }

    /* 需要在此判断当前是否需要关闭socket，没有关闭则一直执行关闭 */
    if (skt_cps.to_close)
    {
        log_i("socket close performing!\r\n");
        if (socket_is_valid(skt_cps.socket_id) 
            &&  (0 ==swei_socket_close(skt_cps.socket_id)))
        {
            log_i("socket[%d] close success!\r\n", skt_cps.socket_id);
            /* 关闭成功 */
            skt_cps.socket_id = SOCKET_INVALID_ID;
            skt_cps.to_close = false;
            skt_cps.last_close_time = swei_getsec();
        } else if (!socket_is_valid(skt_cps.socket_id)) {
            skt_cps.socket_id = SOCKET_INVALID_ID;
            skt_cps.to_close = false;
        }   
    }   

    /* 判断是否需要创建, 需要则创建 MQTT 服务器连接 */
    if (!skt_cps.to_close &&  /* 不需要关闭 */
        skt_cps.is_enabled && /* 需要激活 */
        !socket_is_valid(skt_cps.socket_id))
    { /* 具备IP号 */

        /* 获取服务器地址和端口 */
        swei_socket_host_get((char *)ip, sizeof(ip) - 1, &port, &is_ssl);
        if(is_ssl)
        {
            static unsigned int s_crt_upload_time = 0;

            if (0 == s_crt_upload_time)
                s_crt_upload_time = getsec();

            if ((0 != swei_cert_upload_request(MMGR_UPLOAD_TLS_CA)) 
                        && (!timeout_sec(s_crt_upload_time, 120)))
            {
                return;
            }
        }
        
        if('\0' == ip[0])
        {
            log_w("the server ip is never config!\r\b");
            return;
        }

        // 进行socket创建前先判断信号质量是否获取成功
        if (0 != modem_get_signal_quality(NULL, NULL))
        { /* 未获取信号质量，则清除计时，快速获取信号质量 */
            mmgr_reset_sq_upd_time();
            return;
        }

        if (!swei_timeout_sec(skt_cps.last_close_time, 3))
        { /* 距离上一次关闭socket小于3s则不开启(避免底层MODEM任务没有释放完成) */
            return;
        }

        /* 创建套接字 */
        if (!is_ssl)
        {
            skt_cps.socket_id = swei_socket_open(SWEI_TCP, 
                                                 (const char *)ip, port, 0, CONNECT_MANUAL);
        }
        else
        {
            skt_cps.socket_id = swei_socket_open(SWEI_SSL, 
                                                 (const char *)ip, port, 0, CONNECT_MANUAL);
        }

        if (!socket_is_valid(skt_cps.socket_id))
        { /* 没有获取到正确的ID */
            if (swei_timeout_ms(time_id, 5000))
            { /* 每5s打印一次 */
                time_id = swei_getms();
                log_e("skt_cps.id error = %d\r\n", skt_cps.socket_id);
            }
        }
        else
        {
            log_i("skt_cps.id = %d\r\n", skt_cps.socket_id);
            socket_valid_time = swei_getsec(); /* 创建socket成功，记录当前时间 */
        }

        if (socket_is_valid(skt_cps.socket_id))
        {
            skt_cps.last_socket_id = skt_cps.socket_id; /* 记录当前ID */
            skt_cps.last_close_time = 0;
        }
        else
        {
            if (-EEXIST == skt_cps.socket_id)
            {
                if (swei_timeout_sec(skt_cps.last_close_time, 15))
                {
                    int32_t ret_close = swei_socket_close(skt_cps.last_socket_id);
                    skt_cps.last_close_time = swei_getsec();
                    log_e("MMGR ret_close = %d last id = %d\r\n", ret_close, skt_cps.last_socket_id);
                }
                else
                {
                    skt_cps.socket_id = skt_cps.last_socket_id; /* 重新将ID赋值回来 */
                    log_i("socket id re-assignment, id = %d\r\n", skt_cps.socket_id);
                    return;
                }
            }
            skt_cps.socket_id = SOCKET_INVALID_ID;
        }
    }
}

#include "utils/utils_timer.h"
utils_time_t rtc_sync_timer = {0};
/// @brief 套接字管理程序
/// @param
void swei_socket_process(void)
{
    /* 1. 等待网络配置OK */
    if(!swei_network_is_ready()) {
        return;
    }

    /* 2. 等待证书配置OK */
    swei_cert_upload_process();

    /* 3. 完成socket连接 */
    swei_conn_srv_handler();
}

/// @brief 获取套接字连接状态
/// @param
/// @return
uint8_t swei_socket_connect_ok(void)
{
    return skt_cps.connect_status;
}
